/**
 * @file    main.cpp
 * @author  Goodtea
 * @brief   作业：计算器的实现
 * @date    2023-10-30
 * 
 */

/* 头文件 */
#include <iostream> // 输入输出流（iostream）库
#include <sstream>  // 提供了一个字符串流（stringstream）类模板
#include <fstream>  // 文件 (file) 输入输出操作
#include <stack>    // 实现栈 (stack) 数据结构
#include <queue>    // 实现队列（queue）数据结构
#include <string>   // 操作字符串 (string)
#include <cstddef>  // 定义一些与指针、数组、NULL等有关的类型和宏, 如用来表示对象大小的无符号整数类型 std::size_t, NULL 宏

using namespace std;

/* 定义全局变量 */
stringstream ss;    // 存储每行的后缀表达式
bool error = false; // 判断表达式是否合法

/* 所用函数声明 */
string postfix(string line);                              // 将中缀表达式变为后缀表达式
double postfix_eval_exp(const string& postfix);           // 计算后缀表达式
void _err_exp();                                          // 错误处理函数

/* 主程序 */
int main()
{
    string line;                                          // 存储每一行的字符串
    /* 打开并读取 input.txt 文件 */
    ifstream fin("input.txt",ios::in);                    // 以只读形式打开
    /* 输出 ouput.txt 文件 */
    ofstream fout("output.txt",ios::out | ios::trunc);    // 以只写方式打开文件并在打开文件之前清空文件内容

    /* 检查文件是否正常打开 */
    if (!fin.is_open() || !fout.is_open()) {
        cerr << "could not open input.txt or output.txt" << endl;
        return 1;
    }

    /* 从输入流 fin 中逐行读取数据，并进行一些处理后将结果输出到输出流 fout 中 */
    while (getline(fin,line)) {                           // 从输入流 fin 中不断读取一行数据，并将其存储在字符串变量 line 中
        string postfix_exp = postfix(line);               // 调用 postfix 函数，将字符串 line 转为后缀表达式，并将其存储在 postfix_exp 中
        if (!error) {
            double result = postfix_eval_exp(postfix_exp);
            if (!error) {
                //fout << postfix_exp << " = " << result << endl;
                fout << result << endl;
            } else fout << "Error." << endl;
        } 
        /* 若出现异常 */
        else fout << "Error." << endl;

        /* 初始化全局变量 */
        ss.str("");                                      // 清空字符串流对象 ss 的内容
        error = false;                                   
    }

    /* 关闭相关文件，以释放相关资源，确保文件的正确关闭，并将缓冲区中的数据刷新到文件中 */
    fin.close();
    fout.close();
}

/**
 * @brief 将中缀表达式变为后缀表达式
 * 
 *** 括号相关的说明: 
 * 1 只考虑小括号'()', 不考虑'[]''{}'等其他括号;
 * 2 默认'((...)'合理，即只要在右括号出现前出现了未被匹配的情况即合理
 ** 未考虑'(...)))'的情况 
 ** 未考虑空括号'()'的情况 
 * 
 * @param line 
 * @return string 
 */
string postfix(string line) {
    char c;                                             // 读入缓存区
    stack<char> op;                                     // 操作符栈
    queue<char> exp;                                    // 后缀表达式队列
    bool point = false;                                 // 小数点使用标志, 遇到操作符则变为 false
    int leftbracket = 0;                                // 左括号数目初始值为0
    
    /* 读入中缀表达式  */
    for (size_t i = 0; i < line.length(); i++) {
        /* 处理错误情况 */
        if (error) break;

        char c = line[i];                               // 逐个存储 line 中的元素
        if (c == '\n') break;                           // 若遇到回车，则结束 
        else if (c == '\t' || c == ' ') continue;       // 若遇到制表符和空格，则跳过
        else if (c >= '0'  && c <= '9') exp.push(c);    // 若遇到数字，则直接输出
        else if (c == '.') {                            // 若遇到小数点，则只允许一个小数点
            if (!point) {
                exp.push(c);
                point = true;
            } else _err_exp();
        }
        else if (c == '(') {
            leftbracket ++;
            if (leftbracket == 1) {
                op.push(c);
            }
            point = false;
        }
        else if (c == ')') {                            // 逻辑与括号匹配类似，但只考虑小括号
            if (point) exp.push(' ');
            if (op.empty()) _err_exp();
            else if (!op.empty() && leftbracket > 0) {
                while (!op.empty() && op.top() != '(') { // 运算'()'内的数
                    exp.push(op.top());
                    op.pop();
                }
                leftbracket = 0;                        // 右括号运算完毕
            } else _err_exp();
            op.pop();
            leftbracket = 0;
            point = false;
        }
        else if (c == '+' || c == '-') {                // 加减运算优先级低, 所以清空全部运算符, 直到遇到左括号或者空栈.
           if (point) exp.push(' '); 
           if (!op.empty()) {
                while (!op.empty() && op.top() != '(') {
                    exp.push(op.top());
                    op.pop();
                }
           }
           op.push(c);                                  // 插入当前的 +/- 运算符
           point = false;                               
        }  
        else if (c == '*' || c == '/') {
            if (point) exp.push(' ');
            if (!op.empty()) {
                while (!op.empty() && (op.top() == '*' || op.top() == '/')) {
                    exp.push(op.top());
                    op.pop();
                }  
            }
            op.push(c);
            point = false;
        } 
    }

    /* 入栈剩余的操作符 */
    if (!op.empty()) {
        while (!op.empty()) {
            exp.push(op.top());
            op.pop();
        }
    }
    if (leftbracket != 0) _err_exp();                   // 排除'(('的情况

    /**
     * @brief 
     * 如果是小数部分，直接输出数字; 如果是整数部分，先输出空格，再输出数字；
     * 如果是小数点，末尾再输出一个空格，point = true，遇到其他字符则说明小数部分结束；
     */
    point = false;
    while (!exp.empty()) {
        if (exp.front() >= '0' && exp.front() <= '9') { // 如果遇到数字，连续输出整数/小数位
            // string ss_string = ss.str();
            // char last_element = ss_string[ss_string.size() - 1];
            if (point) {
                ss << exp.front();
                exp.pop();
                continue;
            }
            // else if (last_element >= '0' && last_element <= '9'){
            //     ss << exp.front();
            //     exp.pop();
            //     continue;     
            // }
        }
        ss << ' ' << exp.front(); 
        if (exp.front() == '.') {
            ss << ' ';
            point = true;
            exp.pop();
            continue;
        } 
        exp.pop();
        point = false;
    }
    return ss.str();
}

/**
 * @brief 计算后缀表达式
 * 
 * @param postfix 
 * @return double 
 */
double postfix_eval_exp(const string& postfix) {
    stack<double> operand_stack;                                // 存储后缀表达式中的操作数
    stringstream ss(postfix);                                   // 创建一个新的 std::stringstream 对象，并将 postfix 作为构造函数的参数传递进去
    string element;                                             // 存储后缀表达式中的每个元素（数字、运算符等）
    while (ss >> element) {                                     // 从输入流 ss 中连续读取字符串，并将每个读取的字符串存储到名为 element 的变量中
        /* 处理整数部分 */
        if (isdigit(element[0])) {
            double operand = stod(element);                      // 查找可能的浮点数，并将其转换为 double 类型的值
            operand_stack.push(operand);                         // 入栈
        } 
        /* 处理小数部分 */
        else if (element == ".") {
            string decimal_part;                                 // 存储小数部分
            ss >> decimal_part;                                  // 读取小数点后面的数字串
            double decimal = stod(decimal_part);                 // 将小数部分从字符串转为 double 格式
            /* 得到权重因子*/
            double factor = 1.0;                                 // 权重因子初始值
            for (size_t i = 0; i < decimal_part.length(); i++) { // size_t 是一种无符号整数类型，通常用于表示某个对象或者容器的大小或索引
                factor /= 10;                                    // 得到小数位数权重因子
            }
            /* 整数+小数 */
            double sum = operand_stack.top() + decimal * factor; // 将整数部分与小数部分合并
            operand_stack.pop();                                 // 从栈中弹出并删除顶部整数部分元素
            operand_stack.push(sum);                             // 将新得到的数推入栈
        } 
        /* 处理操作符 */
        else {      
            /* 读入即将进行运算的两个操作数 */
            double operand2 = operand_stack.top();operand_stack.pop();
            double operand1 = operand_stack.top();operand_stack.pop();

            switch (element[0])
            {
            case '+': operand_stack.push(operand1 + operand2);break;
            case '-': operand_stack.push(operand1 - operand2);break;
            case '*': operand_stack.push(operand1 * operand2);break;
            case '/': operand_stack.push(operand1 / operand2);break;
            }
        }
    }
    return operand_stack.top(); 
}

/**
 * @brief 错误处理函数
 * 
 */
void _err_exp() {
    error = true;
}